using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;

using VSTalk.Engine.Core.Context;
using VSTalk.Engine.Core.EntitySession;
using VSTalk.Engine.Core.XmppConnection;
using VSTalk.Engine.Utils;
using VSTalk.Engine.ViewModel.ContactList;
using VSTalk.Engine.ViewModel.Editors;
using VSTalk.Engine.ViewModel.Editors.ClientEditor;
using VSTalk.Engine.Windows;
using VSTalk.Tools;
using VSTalk.Model;

namespace VSTalk.Engine.ViewModel
{
    public class MainViewModel : INotifyPropertyChanged, IListener, IQuerySource
    {
        private ISubscription _clientUnit;
        private string _searchQuery;
        private ISubscription _layoutSwitcherUnit;

        public RelayCommand ChangeConnectionState { get; private set; }

        public RelayCommand ShowAccountsWindow { get; private set; }

        public RelayCommand ShowAddContactWindow { get; private set; }

        public RelayCommand AddNewAccount { get; private set; }

        public RelayCommand ChangeAppSettings { get; private set; }

        public ContactListViewModel ContactList { get; private set; }

        public NotificationViewModel NotificationView { get; private set; }

        public string SearchQuery
        {
            get { return _searchQuery; }
            set
            {
                _searchQuery = value;
                PropertyChanged.Notify(() => SearchQuery);
            }
        }

        private ObservableCollection<Client> Clients
        {
            get { return ModelContext.Clients; }
        }

        private static IModelContext ModelContext
        {
            get { return AppServices.Get<IModelContext>(); }
        }

        private IClientContext GetContext(Client client)
        {
            return SessionModel.GetClientContext(client);
        }

        private static ISessionModel SessionModel
        {
            get { return AppServices.Get<ISessionModel>(); }
        }

        public ClientState CommonState
        {
            get
            {
                if (Clients.Any(client => GetContext(client).State == ClientState.Connecting))
                {
                    return ClientState.Connecting;
                }

                if (Clients.All(client => GetContext(client).State == ClientState.Connected))
                {
                    return ClientState.Connected;
                }

                return ClientState.Disconnected;
            }
        }

        public bool HasConnections
        {
            get { return Clients.Any(); }
        }

        public MainViewModel()
        {
            ContactList = new ContactListViewModel(this);
            NotificationView = new NotificationViewModel();
            SetCommands();
        }

        public void StartListen()
        {
            NotificationView.StartListen();
            ContactList.StartListen();

            _clientUnit = Clients.HandleCollectionChanges(initialOrAdd: (client, p) =>
            {
                PropertyChanged.Notify(() => HasConnections);
                PropertyChanged.Notify(() => CommonState);

                var context = GetContext(client);
                p.Join(context.SubscribeToChange(() => context.State, sender => PropertyChanged.Notify(() => CommonState)));
            }, removed: () =>
            {
                PropertyChanged.Notify(() => HasConnections);
                PropertyChanged.Notify(() => CommonState);                
            });
        }


        public void StopListen()
        {
            _clientUnit.Unsubscribe();
            ContactList.StopListen();
            NotificationView.StopListen();
        }

        private void SetCommands()
        {
            ChangeConnectionState = new RelayCommand(ChangeConnectionStateExecuted);
            ShowAccountsWindow = new RelayCommand(ShowAccountsWindowExecuted);
            ShowAddContactWindow = new RelayCommand(ShowAddContactWindowExecuted);
            ChangeAppSettings = new RelayCommand(ChangeAppSettingsExecuted);
            AddNewAccount = new RelayCommand(AddNewAccountExecuted);
        }

        private void AddNewAccountExecuted()
        {
            var clientVM = new ClientEditorViewModel();
            var clientWindow = new ClientEditorWindow();
            clientWindow.DataContext = clientVM;
            clientVM.Saved += clientWindow.Close;
            clientVM.Rejected += clientWindow.Close;
            clientWindow.Show();
        }

        protected void ChangeAppSettingsExecuted()
        {
            Properties.Settings.Default.Save();
        }

        protected void ShowAddContactWindowExecuted()
        {
            var addContactWindow = new ContactEditorWindow();
            addContactWindow.Show();
        }

        protected void ShowAccountsWindowExecuted()
        {
            var viewModel = new AccountEditorViewModel();
            var accWindow = new AccountEditorWindow();
            accWindow.DataContext = viewModel;
            accWindow.Show();
        }

        public void ChangeConnectionStateExecuted()
        {
            switch (CommonState)
            {
                case ClientState.Disconnected:
                    Connection.ConnectClients();
                    break;
                case ClientState.Connected:
                    Connection.DisconnectClients();
                    break;
            }
        }

        private static IConnectionManager Connection
        {
            get { return AppServices.Get<IConnectionManager>(); }
        }

        public event PropertyChangedEventHandler PropertyChanged;
    }
}